#include <ipl_partition_types.h>
#include "falmain.h"
#include "VirtualDisk.h"

// ++ gump yang
//config and oem reserve
#define OEM_RESERVE_BLOCKS      4       //this is end partition size
#define IPL_CFG_VALID_BLOCK_NUM 3       //must more then two!

/***************************************************************
 *  partition id(identification)
 *
 ***************************************************************/
#define PtMark          (('P'<<0) | ('T'<<8) | ('B'<<16) | ('\0' << 24))
#ifdef OS_WM5
#define IPL_PT_FLASH    (('F'<<0) | ('L'<<8) | ('S'<<16) | ('\0' << 24))        /*wince*/
#else
#define IPL_PT_NK       (('N'<<0) | ('K'<<8) | ('\0'<<16) | ('\0' << 24))       /*wince*/
#define IPL_PT_SYS      (('S'<<0) | ('Y'<<8) | ('S'<<16) | ('\0' << 24))        /*wince*/
#endif
#define IPL_PT_DISK     (('D'<<0) | ('S'<<8) | ('K'<<16) | ('\0' << 24))        /*wince*/

/***************************************************************
 *  partition index
 *
 ***************************************************************/
#ifdef OS_WM5
enum IPL_PT_INDEX
{
    IPL_PT_NBOOT_INDEX = 0,   //index
    IPL_PT_IPL_INDEX    ,   //index
    
    // image partition
    IPL_PT_FLASH_INDEX ,
    IPL_PT_DISK_INDEX   ,
    
    //these index are reserve for ipl and oem
    IPL_PT_CFG_INDEX    ,  //index
    IPL_PT_END_INDEX       //index
};
//#define IPL_PT_IMAGE_INDEX      IPL_PT_ULDR_INDEX   //index
//#define IPL_PT_IMAGE_NUMS       4   //total image partitions
#else
enum IPL_PT_INDEX
{
    IPL_PT_NBOOT_INDEX = 0,   //index
    IPL_PT_IPL_INDEX    ,   //index
    IPL_PT_BAK_INDEX    ,   //index
    
    // image partition
    IPL_PT_ULDR_INDEX   ,
    IPL_PT_NK_INDEX     ,
//    IPL_PT_SYS_INDEX    ,
    //IPL_PT_BFS_INDEX    ,
    //IPL_PT_EXT_INDEX    ,
    IPL_PT_DISK_INDEX   ,
    
    //these index are reserve for ipl and oem
    IPL_PT_CFG_INDEX  ,  //index
    IPL_PT_END_INDEX     //index
};
#define IPL_PT_IMAGE_INDEX      IPL_PT_ULDR_INDEX   //index
#define IPL_PT_IMAGE_NUMS       3   //total image partitions
#endif
#define IPL_PT_RES              2   //total oem reserve
#define TOTAL_PARTITION         (IPL_PT_END_INDEX + 1)   



extern BOOL SyncFMD_GetInfo(PFlashInfo pFlashInfo);
extern DWORD SyncFMD_GetBlockStatus(BLOCK_ID blockID);
extern BOOL FMD_ReadSectorAkVerify(SECTOR_ADDR sectorAddrFMD, LPBYTE pSectorBuff, PSectorInfoAk pSectorInfoBuff, DWORD dwNumSectors);


BOOL GetPtTable(PIPL_PARTITION_TABLE pTable)
{
    if(pTable == NULL)
        return FALSE;
    
    FlashInfo flashInfo;
    if (!SyncFMD_GetInfo(&flashInfo))
    {
        return FALSE;
    }
    
    BOOL ret = FALSE;
    ULONG BlockId;
    ULONG SectorId;
    ULONG LastSector = 0;
    ULONG SearchCnt = 0;

    SectorInfoAk SIr;
    BYTE *pSectorBuf = (BYTE *)LocalAlloc(LPTR, flashInfo.wDataBytesPerSector);
    if (pSectorBuf == NULL)
    {
        ERRORMSG(ZONE_ERROR, (TEXT("Alloc sector buf failed. error %d\r\n"), GetLastError()));
        goto EXIT;
    }

    //search from the last partition table block
    BlockId = flashInfo.dwNumBlocks - OEM_RESERVE_BLOCKS - 1;
    SectorId = BlockId * flashInfo.wSectorsPerBlock;
    
    while(SearchCnt < IPL_CFG_VALID_BLOCK_NUM)
    {
        // if it is the first time to enter this block, we should check its bad mark.
        if (SectorId % flashInfo.wSectorsPerBlock == 0)
        {
            DEBUGMSG(ZONE_ERROR, (TEXT("search block %d! cnt = %d\r\n"), BlockId, SearchCnt));
            //is a bad block?
            if(SyncFMD_GetBlockStatus(BlockId) & BLOCK_STATUS_BAD)
            {
                DEBUGMSG(ZONE_ERROR, (TEXT("Bad Block %d!\r\n"), BlockId));
                goto NEXT_BLOCK;
            }
        }

        //1. read sector
        if(!FMD_ReadSectorAkVerify(SectorId, pSectorBuf, &SIr, 1))
        {
            //need to mark bad block?
            //continue;?
            DEBUGMSG(ZONE_ERROR, (TEXT("Read err @ %d!\r\n"), SectorId));
            goto EXIT;
        }
        
        //2. does this sector has the partition mark?
        if(*(UINT32 *)pSectorBuf == PtMark)
        {// yes, valid id
            DEBUGMSG(ZONE_ERROR, (TEXT("find valid sector %d\r\n"), SectorId));
            
            // remember this sector ID. Only the last valid sector contains the partition table
            LastSector = SectorId;
        }
        else if(*(UINT32 *)pSectorBuf == ~0x0)
        {// this sector is not used (Mark is 0xffffffff)
            break;
        }
        else
        {// this block has some error message!
            DEBUGMSG(ZONE_ERROR, (TEXT("WARN!! Invalid CFG partition.\r\n")));
            goto EXIT;
        }
        
        SectorId++;
        if (SectorId % flashInfo.wSectorsPerBlock) // still in this block
            continue;
        // else, go to next block. fall through
        
NEXT_BLOCK:
        SearchCnt++;
        BlockId--;
        SectorId = BlockId * flashInfo.wSectorsPerBlock;
    }

    if (LastSector == 0)
    {// no valid sector
        DEBUGMSG(ZONE_ERROR, (TEXT("No sector contains partition table.\r\n")));
        goto EXIT;
    }
    
    //Read the last sector
    if(!FMD_ReadSectorAkVerify(LastSector, pSectorBuf, &SIr, 1))
    {
        //need to mark bad block?
        DEBUGMSG(ZONE_ERROR, (TEXT("Read err @ %d!\r\n"), LastSector));
        goto EXIT;
    }

    //get the partition info
    memcpy(pTable, pSectorBuf, sizeof(IPL_PARTITION_TABLE));
    if(pTable->mark != PtMark) // should not happen
    {
        DEBUGMSG(ZONE_ERROR, (TEXT("err3\r\n")));
        goto EXIT;
    }

    DEBUGMSG(ZONE_ERROR, (TEXT("success, locate @ %d\r\n"), LastSector));
    ret = TRUE;
    
EXIT:
    if (pSectorBuf)
    {
        LocalFree(pSectorBuf);
    }
    
    return ret;
}

BOOL GetVirtualDskInfo(DWORD dwId, PVIRTUAL_DISK_INFO pVDInfo)
{
    BOOL ret = FALSE;

    PIPL_PARTITION_TABLE pPt = (PIPL_PARTITION_TABLE)LocalAlloc(LPTR, sizeof(IPL_PARTITION_TABLE));
    if (NULL == pPt)
    {
        ERRORMSG(ZONE_ERROR, (TEXT("Alloc partition table buf failed. error %d\r\n"), GetLastError()));
        goto EXIT;
    }

    // get IPL partition table (Virtual Disk Table)
    if (!GetPtTable(pPt))
    {
        ERRORMSG(ZONE_ERROR, (TEXT("Get partition table failed. error %d\r\n")));
        goto EXIT;
    }
    
    UINT32 VDiskId;

    // get disk ID
    if (0 == dwId)
    {// first CE disk
#ifdef OS_WM5
        VDiskId = IPL_PT_FLASH;
#else
        VDiskId = IPL_PT_NK;
#endif
    }
    else
    {// second CE disk
        VDiskId = IPL_PT_DISK;
    }

    // search disk from partition table
    int index = 0;
    for (index = 0; index < TOTAL_PARTITION; index++)
    {
        if (pPt->PtMsg[index].id == VDiskId)
            break;
    }

    if (index == TOTAL_PARTITION)
        goto EXIT;

    // disk found, retrieve info
    PIPL_PARTITION_MSG pPtMsg = &pPt->PtMsg[index];
    pVDInfo->StartBlock = pPtMsg->StartBlockId;
    pVDInfo->BlockNum = pPtMsg->TotalBlocks;
    if ( (pPtMsg->BadBlockListOffset == INVALID_BBL_INDEX)
        || (pPtMsg->BadBlockCount == 0))
    {// no bad block in this virtual disk
        pVDInfo->BadBlock = NULL;
        pVDInfo->BadBlockNum = 0;
    }
    else
    {
        pVDInfo->BadBlock = (BLOCK_ID*)LocalAlloc(LPTR, pPtMsg->BadBlockCount * sizeof(BLOCK_ID));
        if (NULL == pVDInfo->BadBlock)
        {
            ERRORMSG(ZONE_ERROR, (TEXT("Alloc bad block buf failed. error %d\r\n"), GetLastError()));
            goto EXIT;
        }
        memcpy(pVDInfo->BadBlock, pPt->BadBlockList.list + pPtMsg->BadBlockListOffset, pPtMsg->BadBlockCount * sizeof(BLOCK_ID));
        pVDInfo->BadBlockNum = pPtMsg->BadBlockCount;
    }
    
    ret = TRUE;

EXIT:
    if (pPt)
        LocalFree(pPt);

    return ret;
}



